tree grammar LocScriptGen;

options {
	tokenVocab=LocScript;
	ASTLabelType=LocScriptAST;
	output=template;
}

@header {
	package script.grammar;
	
	import script.grammar.symbol.Symbol;
	import script.grammar.symbol.FieldSymbol;
	import script.grammar.symbol.VariableSymbol;
}

@members {
	Class<?> type;
	String className;
	
	public LocScriptGen(TreeNodeStream in, Class<?> type, String className) {
		this(in);
		this.type = type;
		this.className = className;
	}
}

startFilter
	:	^(FILTER body) -> filterScript(className={className}, typeName={type.getName()}, body={$body.st})
	;
	
startBody
	:	^(FILTER body) -> {$body.st}
	;
	
body:	(s+=statement)* -> body(typeName={type.getName()}, stats={$s})
	;

statement
	:	exprRoot -> stat(stat={$exprRoot.st})
	|	returnStat -> {$returnStat.st}
	|	def -> stat(stat={$def.st})
	|	foreach -> {$foreach.st}
	;
	
def	:	^(DEF a=ID b=ID exprRoot?) -> def(type={$b.symbol.getType().getAlias()}, name={$a.text}, init={$exprRoot.st})
	;
	
foreach
	:	^(FOR a=def b=exprRoot block) -> foreach(def={$a.st}, expr={$b.st}, block={$block.st})
	;
	
returnStat
	:	^(RETURN exprRoot) -> returnStat(expr={$exprRoot.st})
	;
	
block
	:	^(BLOCK (s+=statement)*) -> block(stats={$s})
	;
	
exprRoot:	^(EXPR expr) -> {$expr.st}
	;
	
expr
	:	atom -> {$atom.st}
	|	assign -> {$assign.st}
	|	binaryOp -> {$binaryOp.st}
	|	condOp -> {$condOp.st}
	|	prefixOp -> {$prefixOp.st}
	|	postfixOp -> {$postfixOp.st}
	|	arrayAccess -> {$arrayAccess.st}
	|	memAccess -> {$memAccess.st}
	|	var -> {$var.st}
	;
	
atom
	:	(TRUE
	|	FALSE
	|	INT
	|	FLOAT) {$st=%{$text};}
	|	THIS -> this()
	;
	
op	
@after{$st=%operator(o={$start.getText()});}
	:	PLUS|MINUS|MULT|DIV|MOD
	|	LESS_THAN|GREATER_THAN|LESS_THAN_EQUALS|GREATER_THAN_EQUALS|
	|	EQUALS|NOT_EQUALS
	|	AND|OR
	|	BIT_AND|BIT_OR|XOR
	|	SHIFT_LEFT|SHIFT_RIGHT|U_SHIFT_RIGHT
	;
	
assignOp
@after{$st=%{$start.getText()};}
	:	ASSIGN|PLUS_ASSIGN|MINUS_ASSIGN|MULT_ASSIGN|DIV_ASSIGN
	;
	
assign
	:	^(assignOp var expr) -> assign(op={$assignOp.st}, var={$var.st}, expr={$expr.st})
	;
	
binaryOp
	:	^(op a=expr b=expr) -> binaryOp(op={$op.st}, a={$a.st}, b={$b.st})
	;
	
condOp
	:	^(COND_OP a=expr b=expr c=expr) -> condOp(a={$a.st}, b={$b.st}, c={$c.st})
	;
	
prefixOp
	:	^(NOT a=expr) -> prefixOp(op={"!"}, a={$a.st})
	|	^(NEG a=expr) -> prefixOp(op={"-"}, a={$a.st})
	|	^(BIT_NEG a=expr) -> prefixOp(op={"~"}, a={$a.st})
	|	^(PRE_INC a=expr) -> prefixOp(op={"++"}, a={$a.st})
	|	^(PRE_DEC a=expr) -> prefixOp(op={"--"}, a={$a.st})
	;
	
postfixOp
	:	^(POST_INC a=expr) -> postfixOp(op={"++"}, a={$a.st})
	|	^(POST_DEC a=expr) -> postfixOp(op={"--"}, a={$a.st})
	;
	
arrayAccess
	:	^(ARRAY_ACCESS a=expr b=expr) -> arrayAccess(ar={$a.st}, i={$b.st})
	;
	
memAccess
	:	^(MEM_ACCESS expr ID) -> memberAccess(parent={$expr.st}, id={((FieldSymbol)$ID.symbol).getAlias()})
	;
	
var	:	^(VAR ID) -> {$st=%{((VariableSymbol)$ID.symbol).getAlias()};}
	;